﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Data.SqlClient;
using System.Data;
using System.IO;
using System.Globalization;
using Loggan.Configurations;

namespace Loggan
{   
    public class Logganst
    {
        #region Private Members

        static LogDataTypeEnum LogganstType { get; set; }
        static String LogganstDBConnectionString { get; set; }
        static String LogganSeparateSizeMB { get; set; }
        static String LogganstFileLocation { get; set; }
        static String LogganstFileName { get; set; }
        static bool? IsConfigure = null;

        #endregion

        /// <summary>
        /// Log Message 
        /// </summary>
        /// <param name="message">Message To Log</param>
        /// <param name="fileType">File Type</param>
        /// <param name="fileName">File Name</param>
        /// <param name="logType">Log Type</param>
        public static void LogMessage(String message, Type fileType, String fileName, LogganstType logType)
        {
            /*
             * The Idea From This Method:
             * **************************
             * First Time Application Start, we want to check if there is table or file, 
             * so loggan Can log Message Normally without any reconfigure.
             * 
             * For First Time IsConfigure is Null as default Value.
             * So We will Call CheckForConfiguration() Method To Check If There is and Configuration or not, if return false
             * we will Call Configure() Method To Configure Every Thing, else Log Normally Without any configuration.
             * 
             * If We Call Log Method Again 
             * IsConfigure Will Equal True, So We Will Escape All Check And Log Directly
            */

            //Check IsConfigure null or not 
            if (IsConfigure == null)
            {
                //call CheckForConfiguration and set IsConfigure
                IsConfigure = CheckForConfiguration();
            }

            //If IsConfigure is false or true
            if (!IsConfigure.Value)
            {
                //Call Configure Method
                Configure();
            }

            //Log The Message
            if (LogganstType == LogDataTypeEnum.Database)
                InsertNewMessageInDB(message, fileType, fileName, logType);
            else if (LogganstType == LogDataTypeEnum.FileSystem)
                InsertNewMessageInFile(message, fileType, fileName, logType);
            else
                throw new Exception("Something Wrong ... Log Type is not Exist.");

        }

        #region Private Methods

        /// <summary>
        /// Check For Configuration 
        /// </summary>
        /// <returns></returns>
        private static bool CheckForConfiguration()
        {
            /*
             * The Idea From This Method:
             * **************************
             * Get The Configuration Section From Web.config or App.Config 
             * And Check For Required Keys And Type
            */

            //Get keys from web.config
            LogganConfigurationSection loggerConfig = ConfigurationManager.GetSection("LogganConfiguration") as LogganConfigurationSection;

            if (loggerConfig != null)
            {
                try
                {
                    //Get Loggan Type From Configuration (Database Or Text File)
                    string logType = (loggerConfig.Configs["LogganType"].Value);

                    //Check For TextFile Or Database and set LogganstType Variable
                    if (logType == "TextFile")
                        LogganstType = LogDataTypeEnum.FileSystem;
                    else if (logType == "Database")
                        LogganstType = LogDataTypeEnum.Database;
                    else
                    {
                        throw new Exception("LogganType Key has no acceptable value ... value should be 'TextFile' OR 'Database'.");
                    }

                }
                catch
                {
                    throw new Exception("LogganType Key is no exist.");
                }
            }
            else
            {
                throw new Exception("LogganConfiguration Section Not Found Under configSections Section Under configuration Section");
            }

            //Check If LogganstType = Database Check For ConnectionString Key
            if (LogganstType == LogDataTypeEnum.Database)
            {
                try
                {
                    LogganstDBConnectionString = (loggerConfig.Configs["LogganDBConnectionString"].Value);
                }
                catch
                {
                    throw new Exception("LogganDBConnectionString Key is no exist.");
                }

            }
            //Check If LogganstType = FileSystem, Check For File Location, File Name and if there is Separate Size or not
            else if (LogganstType == LogDataTypeEnum.FileSystem)
            {
                try
                {
                    LogganstFileLocation = (loggerConfig.Configs["LogganFileLocation"].Value);
                }
                catch
                {
                    throw new Exception("LogganFileLocation Key is no exist.");
                }

                try
                {
                    LogganstFileName = (loggerConfig.Configs["LogganFileName"].Value);
                }
                catch
                {
                    throw new Exception("LogganFileName Key is no exist.");
                }

                try
                {
                    LogganSeparateSizeMB = (loggerConfig.Configs["LogganSeparateSizeMB"].Value);
                }
                catch
                {
                    LogganSeparateSizeMB = null;
                }
            }
            else
            {
                throw new Exception("Something Wrong ... Log Type is not Exist not Exist.");
            }

            //Check For Table In DB Or Check For Text File If Exist
            bool IsCreated = false;
            if (LogganstType == LogDataTypeEnum.Database)
                IsCreated = CheckTableIsCreated();
            else if (LogganstType == LogDataTypeEnum.FileSystem)
                IsCreated = CheckFileIsCreated();

            //Return True If Exist Else Return False;
            return IsCreated;
        }

        /// <summary>
        /// Check If File Exist Or Not
        /// </summary>
        /// <returns></returns>
        private static bool CheckFileIsCreated()
        {
            String fileLocation = null;
            bool IsCreated = false;
            if (!String.IsNullOrEmpty(LogganstFileLocation) && !String.IsNullOrEmpty(LogganstFileName))
            {
                //Check If File Is Exist Or Not
                fileLocation = LogganstFileLocation + @"\" + LogganstFileName;
                IsCreated = File.Exists(fileLocation);
            }
            else
                throw new Exception("Something Wrong ... File Location or File Name is not Exist.");

            return IsCreated;
        }

        /// <summary>
        /// Check If Table In DB Is Exist Or Not
        /// </summary>
        /// <returns></returns>
        private static bool CheckTableIsCreated()
        {
            String tableName = "LogganTicket";
            bool tableExists = false;
            if (!String.IsNullOrEmpty(LogganstDBConnectionString))
            {
                SqlConnection connection = new SqlConnection(LogganstDBConnectionString);

                //Check Using Simple Query if Table Exist Or Not.
                StringBuilder cmd = new StringBuilder();
                cmd.Append("select case when exists((select * from information_schema.tables where table_name = '");
                cmd.Append(tableName);
                cmd.Append("')) then 1 else 0 end");

                SqlCommand command = new SqlCommand(cmd.ToString(), connection);
                connection.Open();
                tableExists = (int)command.ExecuteScalar() == 1;
                command.Dispose();
                connection.Close();
            }
            else
                throw new Exception("Something Wrong ... Connection String not Exist.");

            return tableExists;
        }

        /// <summary>
        /// Configure Method
        /// Contain CreateDB And CreateFile
        /// </summary>
        private static void Configure()
        {
            //Create File or DB table 
            if (LogganstType == LogDataTypeEnum.Database)
            {
                if (!String.IsNullOrEmpty(LogganstDBConnectionString))
                    CreateDBTable();
                else
                    throw new Exception("Something Wrong ... Connection String not Exist.");
            }
            else if (LogganstType == LogDataTypeEnum.FileSystem)
            {
                if (!String.IsNullOrEmpty(LogganstFileLocation) && !String.IsNullOrEmpty(LogganstFileName))
                    CreateFile();
                else
                    throw new Exception("Something Wrong ... File Location or File Name is not Exist not Exist.");
            }
            else
            {
                throw new Exception("Something Wrong ... Log Type is not Exist not Exist.");
            }

            IsConfigure = true;
        }

        /// <summary>
        /// Create File Method
        /// </summary>
        private static void CreateFile()
        {
            String fileLocation = null;
            if (!String.IsNullOrEmpty(LogganstFileLocation) && !String.IsNullOrEmpty(LogganstFileName))
            {
                fileLocation = LogganstFileLocation + LogganstFileName;
                var logganFile = File.Create(fileLocation);
                logganFile.Close();
            }
            else
                throw new Exception("Something Wrong ... File Location or File Name is not Exist not Exist.");

        }

        /// <summary>
        /// Create Table In Database
        /// </summary>
        private static void CreateDBTable()
        {
            SqlConnection connection = new SqlConnection(LogganstDBConnectionString);
            SqlCommand command = new SqlCommand();

            try
            {
                //Query .. Check If LogganTicket Not Exist .. Create It
                string cmdCreateLogganTable = @"IF  NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[LogganTicket]') AND type in (N'U'))

                                BEGIN
                                CREATE TABLE [dbo].[LogganTicket](
                                    LogganID int IDENTITY(1,1) Primary Key,
                                    LogganMessage nvarchar(MAX),
                                    LogganFileType nvarchar(MAX),
                                    LogganFileName nvarchar(MAX),
                                    LogganType nvarchar(20),
                                    LogganDate datetime
                                ) 
                                END";

                if (connection.State == ConnectionState.Closed)
                    connection.Open();

                command = new SqlCommand(cmdCreateLogganTable, connection);
                command.CommandType = CommandType.Text;
                command.ExecuteNonQuery();
                command.Dispose();
                connection.Close();
            }
            catch (Exception exp)
            {
                connection.Close();
                throw exp;
            }
        }

        /// <summary>
        /// Insert Method That Log message Into Table LogganTicket In DB
        /// </summary>
        /// <param name="message">Message To Log</param>
        /// <param name="fileType">File Type</param>
        /// <param name="fileName">File Name</param>
        /// <param name="logType">Log Type</param>
        private static void InsertNewMessageInDB(String message, Type fileType, String fileName, LogganstType logType)
        {
            SqlConnection connection = new SqlConnection(LogganstDBConnectionString);
            SqlCommand command = new SqlCommand();

            try
            {
                StringBuilder cmdInsertNewRecord = new StringBuilder();
                cmdInsertNewRecord.Append("insert into [dbo].[LogganTicket] values ('");
                cmdInsertNewRecord.Append(message);
                cmdInsertNewRecord.Append("', '");
                cmdInsertNewRecord.Append(fileType.Name);
                cmdInsertNewRecord.Append("', '");
                cmdInsertNewRecord.Append(fileName);
                cmdInsertNewRecord.Append("', '");
                cmdInsertNewRecord.Append(logType.ToString());
                cmdInsertNewRecord.Append("', '");
                CultureInfo ci = new CultureInfo("EN-US");
                cmdInsertNewRecord.Append(TimeZoneInfo.ConvertTimeFromUtc(DateTime.Now.ToUniversalTime(), TimeZoneInfo.FindSystemTimeZoneById(ConfigurationManager.AppSettings["TimeZoneRegion"])).ToString(ci));
                cmdInsertNewRecord.Append("')");

                if (connection.State == ConnectionState.Closed)
                    connection.Open();

                command = new SqlCommand(cmdInsertNewRecord.ToString(), connection);
                command.CommandType = CommandType.Text;
                command.ExecuteNonQuery();
                command.Dispose();
                connection.Close();
            }
            catch (Exception exp)
            {
                connection.Close();
                throw exp;
            }
        }

        /// <summary>
        /// Insert Method That Log message Into Text File
        /// </summary>
        /// <param name="message">Message To Log</param>
        /// <param name="fileType">File Type</param>
        /// <param name="fileName">File Name</param>
        /// <param name="logType">Log Type</param>
        private static void InsertNewMessageInFile(string message, Type fileType, string fileName, Loggan.LogganstType logType)
        {
            /*
            * The Idea From This Method:
            * **************************
            * Check First If Separate Key Exist Or Not if not exist it will log normally in exist file.
            * Else if key is exist we will check for all fils that have .txt extention in folder that we set it in config file.
             * 
             * We Check Also If No File In Folder, we Will Create New One And Insert Into It The Log Message
             * 
            * Get Newest File By Date and check for it's size
            * If File Has Size More Than the size in Config File, we will create new file and insert into it the log message
            * Else We Will Log Message Normally in current file 
           */

            // Check For LogganSeparateSizeMB Key if Contain Value Or Not
            // If Not Contain Value It Mean User Don't Want To Separate So We Will Add Log Normally In Else
            if (!String.IsNullOrEmpty(LogganSeparateSizeMB))
            {
                //Check If The Value Is Float Or Not
                float configSize;
                bool IsConfigSize = float.TryParse(LogganSeparateSizeMB, out configSize);
                if (IsConfigSize)
                {
                    // Get All Files that End With .txt
                    var ext = new List<string> { ".txt" };
                    var myFiles = Directory.GetFiles(LogganstFileLocation, "*.*", SearchOption.TopDirectoryOnly).Where(s => ext.Any(e => s.EndsWith(e)));

                    //Check If Directory Contain Files Or Not
                    // If No File - Create New File And Insert Into It
                    if (myFiles.Count() > 0)
                    {
                        // Get Any File 
                        FileInfo lastFile = new FileInfo(myFiles.FirstOrDefault());

                        //Loop For All Files To Get The Lates One By Date
                        foreach (var file in myFiles)
                        {
                            FileInfo fi = new FileInfo(file);
                            if (lastFile.CreationTime < fi.CreationTime)
                                lastFile = fi;
                        }

                        //Check Latest File If Its Size More Than The LogganSeparateSizeMB (Key In Config. File) or Not 
                        if (lastFile.Length >= configSize * 1024 * 1024)
                        {
                            // Create New File With Date.Now
                            string _file = LogganstFileLocation + "\\" + LogganstFileName.Remove(LogganstFileName.Length - 4) + "_" + DateTime.Now.ToString("dd_MM_yyyy___HH_mm") + ".txt";
                            var logganFile = File.Create(_file);
                            logganFile.Close();

                            WriteInFile(message, fileType, fileName, logType, _file);
                        }
                        else
                        {
                            WriteInFile(message, fileType, fileName, logType, lastFile.FullName);
                        }
                    }
                    else
                    {
                        string _file = LogganstFileLocation + "\\" + LogganstFileName;
                        var logganFile = File.Create(_file);
                        logganFile.Close();

                        WriteInFile(message, fileType, fileName, logType, _file);
                    }
                }
                else
                {
                    throw new Exception("LogganSeparateSizeMB Key is no Float or Integer.");
                }
            }
            else
            {
                string _file = LogganstFileLocation + "\\" + LogganstFileName;
                WriteInFile(message, fileType, fileName, logType, _file);
            }

            #region another way
            ////////////////////////////////////Nehal\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

            //String fileLocation = null;
            //if (!String.IsNullOrEmpty(LogganstFileLocation) && !String.IsNullOrEmpty(LogganstFileName))
            //{
            //    fileLocation = LogganstFileLocation + "\\" + LogganstFileName;
            //    if (!String.IsNullOrEmpty(LogganSeparateSizeMB))
            //    {
            //        float configSize;
            //        bool IsConfigSize = float.TryParse(LogganSeparateSizeMB, out configSize);
            //        if (IsConfigSize)
            //        {
            //            if (!String.IsNullOrEmpty(fileLocation))
            //            {
            //                var myFile = File.Open(fileLocation, FileMode.Open, FileAccess.Write);
            //                if (myFile.Length >= configSize * 1024 * 1024)
            //                {
            //                    string newFileNme = LogganstFileLocation + "\\" + LogganstFileName.Remove(LogganstFileName.Length - 4) + "_" + DateTime.Now.ToString("dd_MM_yyyy___HH_mm") + ".txt";
            //                    File.Move(fileLocation, newFileNme);
            //                    var logganFile = File.Create(fileLocation);
            //                    logganFile.Close();
            //                }
            //                myFile.Close();
            //            }
            //        }
            //        else
            //        {
            //            throw new Exception("LogganSeparateSizeMB Key is no Float or Integer.");
            //        }
            //    }

            //    WriteInFile(message, fileType, fileName, logType, fileLocation);

            //}
            #endregion
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message">Message To Log</param>
        /// <param name="fileType">File Type</param>
        /// <param name="fileName">File Name</param>
        /// <param name="logType">Log Type</param>
        /// <param name="fileLocation">File Location</param>
        private static void WriteInFile(string message, Type fileType, string fileName, Loggan.LogganstType logType, string fileLocation)
        {
            //Open The File 
            using (StreamWriter w = File.AppendText(fileLocation))
            {
                //Build The String To Be Insert
                StringBuilder newLog = new StringBuilder();
                newLog.Append(logType.ToString());
                newLog.Append(" -- ");
                newLog.Append(fileType.Name);
                newLog.Append(" -- ");
                newLog.Append(fileName);
                newLog.Append(" -- ");
                newLog.Append(message);
                newLog.Append(" -- ");
                CultureInfo ci = new CultureInfo("EN-US");
                newLog.Append(TimeZoneInfo.ConvertTimeFromUtc(DateTime.Now.ToUniversalTime(), TimeZoneInfo.FindSystemTimeZoneById(ConfigurationManager.AppSettings["TimeZoneRegion"])).ToString(ci));
                newLog.Append(Environment.NewLine);
                newLog.Append(" -------------------------------------------------------------------");
                w.WriteLine(newLog.ToString());
            }
        }

        #endregion
    }
}
